using System;
using System.Collections.Generic;
using MarkovCalibrationChain;
namespace LANDIS_II_Debug_Console
{
    class Program
    {
        const string SpeciesParametersPath = "SpeciesParameters.txt";

        static double MaxLogProb = double.MinValue;
        static string WorkingDirectory = System.IO.Directory.GetCurrentDirectory();

        static int Iteration = 0;
        static bool CheckParameters(Parameters parameters)
        {
            foreach (Parameter p in parameters.ModelParameters)
            {
                if (p.OutOfRange()) return false;
            }
            return true;
        }

        static int GetColumnNumber(string HeaderLine, string ColumnHeader)
        {
            string[] terms = HeaderLine.Split(new char[] { '\t', ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);

            for (int t = 0; t < terms.Length; t++) if (terms[t].Contains(ColumnHeader)) return t;

            throw new System.Exception("Cannot find columnheader " + ColumnHeader);

        }
        static void SetInSpeciesParameterTable(string Species, string ColumnHeader, double value)
        {

            if (System.IO.File.Exists(SpeciesParametersPath) == false) throw new System.Exception("Speciestable " + SpeciesParametersPath + " does not exist");

            string[] content = System.IO.File.ReadAllLines(SpeciesParametersPath);

            List<string> NewContent = new List<string>();
            for (int line = 0; line < content.Length; line++)
            {
                if (line < 2)
                {
                    NewContent.Add(content[line]);
                    continue;
                }

                if (Species == null || content[line].Contains(Species))
                {
                    string[] terms = content[line].Split('\t');

                    terms[GetColumnNumber(content[1], ColumnHeader)] = value.ToString();

                    string newline = "";

                    foreach (string t in terms) newline += t + "\t";

                    NewContent.Add(newline);
                }

                else NewContent.Add(content[line]);
            }


            System.IO.File.WriteAllLines(SpeciesParametersPath, NewContent.ToArray());

        }
        static void ApplyParameters(Parameters parameters)
        {
            foreach (Parameter parameter in parameters.ModelParameters)
            {
                string[] terms = parameter.Label.Split('_');
                string parameterlabel = terms[0];

                string species = null;
                if (terms.Length > 1) species = terms[1];

                SetInSpeciesParameterTable(species, parameterlabel, parameter.RunningValue);
            }
        }
        static float GetMax(List<float> A)
        {
            float max = float.MinValue;
            foreach (float a in A) if (a > max) max = a;
            return max;
        }
        static int GetMax(List<int> A)
        {
            int max = int.MinValue;
            foreach (int a in A) if (a > max) max = a;
            return max;
        }
        static void SetSimulationLength(int Duraction)
        {
            List<string> newcontent = new List<string>();
            foreach (string line in System.IO.File.ReadAllLines("scenario.txt"))
            {
                if (line.Contains("Duration"))
                {
                    string[] terms = line.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
                    terms[1] = Duraction.ToString();
                    string newline = "";
                    foreach (string t in terms) newline += t + '\t';
                    newcontent.Add(newline);
                }
                else newcontent.Add(line);
            }
            System.IO.File.WriteAllLines("scenario.txt", newcontent.ToArray());
        }
        /*
        static void SetSimulationLength(List<int> Ages)
        {
            List<string> newcontent = new List<string>();
            foreach (string line in System.IO.File.ReadAllLines("scenario.txt"))
            {
                if (line.Contains("Duration"))
                {
                    string[] terms = line.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);
                    terms[1] = (GetMax(Ages) + 1).ToString();
                    string newline = "";
                    foreach (string t in terms) newline += t + '\t';
                    newcontent.Add(newline);
                }
                else newcontent.Add(line);
            }
            System.IO.File.WriteAllLines("scenario.txt", newcontent.ToArray());
        }
         */
        static void SetInitialCommunities(List<string> Species, List<List<int>> Ages)
        {
            /*
             * List<string> UniqueSpecies = GetUniqueSpecies(Measurements);
                List<List<int>> Ages = GetAges(UniqueSpecies, Measurements);
                List<List<float>> Biomass = GetBiomass(UniqueSpecies, Measurements);
             */
            List<string> Content = new List<string>();
            Content.Add("LandisData \"Initial Communities\"");
            Content.Add("MapCode    0");
            Content.Add("MapCode    1");

            for (int spc = 0; spc < Species.Count; spc++)
            {
                string line = Species[spc] + '\t' + GetMax(Ages[spc]);

                //foreach (int age in Ages[spc])
                //{
                //    line += "\t" + age;
                //}
                Content.Add(line);
            }


            System.IO.File.WriteAllLines(WorkingDirectory + "\\initial-communities.txt", Content.ToArray());

        }
        static string GetValue(string[] FileContent, int r, int c)
        {
            return FileContent[r].Split('\t')[c];
        }
        static List<float> GetModeled(string SiteFolder, string Species, string Header)
        {
            List<float> Modeled = new List<float>();
            string[] outputfiles = System.IO.Directory.GetFiles(SiteFolder + "\\output\\PNEToutputsites");

            foreach (string file in outputfiles)
            {
                if (file.Contains(Species))
                {
                    string[] FileContent = System.IO.File.ReadAllLines(file);
                    int ColumnNumber = GetColumnNumber(FileContent[0], Header);

                    for (int Line = 1; Line < FileContent.Length; Line++)
                    {
                        Modeled.Add(float.Parse(FileContent[Line].Split(',')[ColumnNumber]));
                    }

                }

            }
            return Modeled;
        }

        static void WriteModelMeas(string FileName, string species, List<int> MeasuredAges, List<float> MeasuredBiomass, List<float> ModeledBiomass, ref double LogProbability)
        {

            List<string> FileContent = new List<string>();

            bool[] measurementfound = new bool[MeasuredAges.Count];

            FileContent.Add("Age\tModel\tMeas");
            for (int r = 0; r < ModeledBiomass.Count; r++)
            {
                string Age = Math.Round(r / 12.0, 1).ToString();

                string Line = Age + "\t" + ModeledBiomass[r] + "\t";

                for (int m = 0; m < MeasuredAges.Count; m++)
                {
                    if (MeasuredAges[m].ToString() == Age)
                    {
                        Line += MeasuredAges[m] + '\t' + MeasuredBiomass[m];

                        LogProbability += Math.Log(1.0 / Math.Pow(MeasuredBiomass[m] - ModeledBiomass[r], 2));

                        measurementfound[m] = true;
                    }

                }
                FileContent.Add(Line);
            }

            System.IO.File.WriteAllLines(FileName, FileContent.ToArray());

            foreach (bool f in measurementfound) if (f == false) LogProbability = double.MinValue;

        }
        static List<string> GetUniqueSpecies(string[] Measurements)
        {
            List<string> UniqueSpecies = new List<string>();

            int colSpecies = GetColumnNumber(Measurements[0], "Species");
            int colAge = GetColumnNumber(Measurements[0], "Age");
            int colBiom = GetColumnNumber(Measurements[0], "Biomass");



            for (int line = 1; line < Measurements.Length; line++)
            {
                string species = Measurements[line].Split(new char[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries)[colSpecies];

                bool unique = true;
                foreach (string spc in UniqueSpecies)
                {
                    if (spc == species) unique = false;
                }
                if (unique)
                {
                    UniqueSpecies.Add(species);
                }
            }
            return UniqueSpecies;
        }
        static List<List<int>> GetAges(List<string> UniqueSpecies, string[] Measurements)
        {
            int colSpecies = GetColumnNumber(Measurements[0], "Species");
            int colAge = GetColumnNumber(Measurements[0], "Age");
            int colBiom = GetColumnNumber(Measurements[0], "Biomass");

            List<List<int>> Ages = new List<List<int>>();

            foreach (string spc in UniqueSpecies)
            {
                Ages.Add(new List<int>());
            }

            for (int line = 1; line < Measurements.Length; line++)
            {
                string species = Measurements[line].Split(new char[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries)[colSpecies];
                int Age = int.Parse(Measurements[line].Split(new char[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries)[colAge]);
                for (int spc = 0; spc < UniqueSpecies.Count; spc++)
                {
                    if (UniqueSpecies[spc] == species)
                    {
                        Ages[spc].Add(Age);
                    }
                }
            }

            return Ages;
        }
        static List<List<float>> GetBiomass(List<string> UniqueSpecies, string[] Measurements)
        {
            int colSpecies = GetColumnNumber(Measurements[0], "Species");
            int colAge = GetColumnNumber(Measurements[0], "Age");
            int colBiom = GetColumnNumber(Measurements[0], "Biomass");

            List<List<float>> Biomass = new List<List<float>>();

            foreach (string spc in UniqueSpecies)
            {
                Biomass.Add(new List<float>());
            }

            for (int line = 1; line < Measurements.Length; line++)
            {
                string species = Measurements[line].Split(new char[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries)[colSpecies];
                float biomass = float.Parse(Measurements[line].Split(new char[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries)[colBiom]);

                for (int spc = 0; spc < UniqueSpecies.Count; spc++)
                {
                    if (UniqueSpecies[spc] == species)
                    {
                        Biomass[spc].Add(biomass);
                    }
                }
            }

            return Biomass;
        }

        static void RemoveDirectory(string dir)
        {
            if (System.IO.Directory.Exists(dir) == false) return;

            foreach (string subdir in System.IO.Directory.GetDirectories(dir))
            {
                RemoveDirectory(subdir);
            }
            foreach (string file in System.IO.Directory.GetFiles(dir))
            {
                System.IO.File.Delete(file);
            }
            if (System.IO.Directory.Exists(dir)) System.IO.Directory.Delete(dir);
        }
        static double GetLogProbability(Parameters p)
        {
            double LogProbability = 0;

            string[] Sites = System.IO.Directory.GetDirectories(WorkingDirectory);

            ApplyParameters(p);

            List<string> ModelMeasFN = new List<string>();
            foreach (string site in Sites)
            {
                System.IO.Directory.SetCurrentDirectory(site);

                RemoveDirectory("output");

                string[] Measurements = System.IO.File.ReadAllLines("measurements.txt");

                List<string> UniqueSpecies = GetUniqueSpecies(Measurements);
                List<List<int>> Ages = GetAges(UniqueSpecies, Measurements);
                List<List<float>> Biomass = GetBiomass(UniqueSpecies, Measurements);

                SetInitialCommunities(UniqueSpecies, Ages); ;

                SetSimulationLength(1);

                Landis.App.Main(new string[] { "scenario.txt" });


                for (int s = 0; s < UniqueSpecies.Count; s++)
                {

                    List<float> ModeledBiomass = GetModeled(site, UniqueSpecies[s], "Wood(gDW_m2)");
                    List<float> ModeledAge = GetModeled(site, UniqueSpecies[s], "Age");


                    string fn = site + "\\ModelMeas" + UniqueSpecies[s] + ".txt";
                    WriteModelMeas(fn, UniqueSpecies[s], Ages[s], Biomass[s], ModeledBiomass, ref LogProbability);
                    ModelMeasFN.Add(fn);

                    if (LogProbability <= float.MinValue)
                    {
                        break;
                    }

                    foreach (string File in System.IO.Directory.GetFiles("output"))
                    {
                        System.IO.File.Delete(File);

                    }


                }
                if (LogProbability <= float.MinValue)
                {
                    break;
                }

            } 

            if (LogProbability > MaxLogProb)
            {
                MaxLogProb = LogProbability;

                System.IO.File.Copy(SpeciesParametersPath, "BestSpeciesParameters");

                foreach (string fn in ModelMeasFN)
                {
                    System.IO.File.Copy(fn, fn.Replace(".txt", "opt.txt"), true);
                }

            }


            System.IO.Directory.SetCurrentDirectory(WorkingDirectory);

             
            Iteration++;

            return LogProbability;
        }
        static List<string> GetSpecies()
        {
            List<string> Species = new List<string>();
            string[] Filecontent = System.IO.File.ReadAllLines("SpeciesParameters.txt");

            for (int line = 2; line < Filecontent.Length; line++)
            {
                Species.Add(Filecontent[line].Split(new char[] { '\t' }, StringSplitOptions.RemoveEmptyEntries)[0]);

            }
            return Species;
        }
        static double GetInitialParameter(string filename, string label, double defaul_value)
        {
            if (System.IO.File.Exists(filename) == false) return defaul_value;

            string[] content = System.IO.File.ReadAllLines(filename);

            string[] labels = content[0].Split(new char[] { '\t', ',' }, StringSplitOptions.RemoveEmptyEntries);

            string[] values = content[1].Split(new char[] { '\t', ',' }, StringSplitOptions.RemoveEmptyEntries);

            for (int s = 0; s < labels.Length; s++)
            {
                if (labels[s] == label) return double.Parse(values[s]);
            }

            return defaul_value;
        }
        static void Main(string[] args)
        {

            Parameters parameters = new MarkovCalibrationChain.Parameters(CheckParameters);

            List<string> Species = GetSpecies();

            string Fn_initials = "InitialParameters.txt";
            foreach (string spc in Species)
            {
                parameters.Add("GrMstSens_" + spc, 0.1, 5, GetInitialParameter(Fn_initials, "GrMstSens_" + spc, 0.2));
                parameters.Add("AmaxA_" + spc, -100, 100, GetInitialParameter(Fn_initials, "AmaxA_" + spc, 80));
            }
            parameters.Add("MaintResp", 0, 0.05, GetInitialParameter(Fn_initials, "MaintResp", 0.001));
            parameters.Add("AmaxB", 0, 300, GetInitialParameter(Fn_initials, "AmaxB", 71.9));
            parameters.Add("TOwood", 0, 0.05, GetInitialParameter(Fn_initials, "TOwood", 0.01));
            parameters.Add("DNSC", 0, 0.4, GetInitialParameter(Fn_initials, "TOwood", 0.0));

 
            MarkovCalibrationChain.MCMC mcmc = new MarkovCalibrationChain.MCMC(parameters, GetLogProbability);

            mcmc.CreateOutputFile("mcmc_progress.txt");

            mcmc.RunMCMC(int.MaxValue, 0.05F);
         
           



        }
    }
}
